/*
 ------------------------------------------------------------------------------
 Copyright (C) 2009-2011 Eternal Games.

 This file is part of the Eternal Tech source code.

 The Eternal Tech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Eternal Tech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Eternal Tech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// r_interaction.cpp - Light interaction rendering
//


// TODO: Rewrite this mess!!!


#include "r_local.h"


/*
 ==============================================================================

    STANDARD SINGLE-PASS GLSL RENDERING

 ==============================================================================
*/


/*
 ==================
 RB_RenderInteraction
 ==================
*/
void RB_RenderInteraction (stage_t *bumpStage, stage_t *diffuseStage, stage_t *specularStage)
{
	texture_t		  *bumpTexture, *diffuseTexture, *specularTexture;
	program_t		  *program;
	mat4_t			  bumpMatrix, diffuseMatrix, specularMatrix;
	vec3_t		      diffuseColor, specularColor;
	float		      lightScale, specularPower, specularScale, colorModulate = 0.0f, colorAdd = 1.0f;

	if (r_skipBump->integerValue)
		bumpStage = NULL;
	if (r_skipDiffuse->integerValue)
		diffuseStage = NULL;
	if (r_skipSpecular->integerValue)
		specularStage = NULL;

	// Get the light scale
	if (r_lightScale->floatValue > 1.0f)
		lightScale = r_lightScale->floatValue;
	else
		lightScale = 1.0f;

	// Set up color modulate/add
	if (diffuseStage && specularStage)
	{
		if (diffuseStage->colorStage.vertexColor == VC_MODULATE && specularStage->colorStage.vertexColor == VC_MODULATE)
		{
			colorModulate = 1.0f;
			colorAdd = 0.0f;
		}
		else if (diffuseStage->colorStage.vertexColor == VC_INVERSE_MODULATE && specularStage->colorStage.vertexColor == VC_INVERSE_MODULATE)
		{
			colorModulate = -1.0f;
			colorAdd = 1.0f;
		}
	}
	else
	{
		if (diffuseStage)
		{
			if (diffuseStage->colorStage.vertexColor == VC_MODULATE)
			{
				colorModulate = 1.0f;
				colorAdd = 0.0f;
			}
			else if (diffuseStage->colorStage.vertexColor == VC_INVERSE_MODULATE)
			{
				colorModulate = -1.0f;
				colorAdd = 1.0f;
			}
		}

		if (specularStage)
		{
			if (specularStage->colorStage.vertexColor == VC_MODULATE)
			{
				colorModulate = 1.0f;
				colorAdd = 0.0f;
			}
			else if (specularStage->colorStage.vertexColor == VC_INVERSE_MODULATE)
			{
				colorModulate = -1.0f;
				colorAdd = 1.0f;
			}
		}
	}

	// Set up the bump texture and matrix
	if (bumpStage)
	{
		bumpTexture = bumpStage->textureStage.texture;

		RB_MultiplyTextureMatrix(backEnd.material, &bumpStage->textureStage, bumpMatrix);
	}
	else
	{
		bumpTexture = rg.flatTexture;

		Matrix4_Identity(bumpMatrix);
	}

	// Set up the diffuse texture, matrix, and color
	if (diffuseStage)
	{
		diffuseTexture = diffuseStage->textureStage.texture;

		RB_MultiplyTextureMatrix(backEnd.material, &diffuseStage->textureStage, diffuseMatrix);

		diffuseColor[0] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[0]] * lightScale;
		diffuseColor[1] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[1]] * lightScale;
		diffuseColor[2] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[2]] * lightScale;

		diffuseColor[0] *= backEnd.material->expressionRegisters[diffuseStage->colorStage.registers[0]];
		diffuseColor[1] *= backEnd.material->expressionRegisters[diffuseStage->colorStage.registers[1]];
		diffuseColor[2] *= backEnd.material->expressionRegisters[diffuseStage->colorStage.registers[2]];
	}
	else
	{
		diffuseTexture = rg.whiteTexture;

		Matrix4_Identity(diffuseMatrix);

		diffuseColor[0] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[0]] * lightScale;
		diffuseColor[1] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[1]] * lightScale;
		diffuseColor[2] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[2]] * lightScale;
	}

	// Set up the specular texture, matrix, color, and exponent
	if (specularStage)
	{
		specularTexture = specularStage->textureStage.texture;

		RB_MultiplyTextureMatrix(backEnd.material, &specularStage->textureStage, specularMatrix);

		specularColor[0] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[0]] * lightScale;
		specularColor[1] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[1]] * lightScale;
		specularColor[2] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[2]] * lightScale;

		specularColor[0] *= backEnd.material->expressionRegisters[specularStage->colorStage.registers[0]] * 2.0f;
		specularColor[1] *= backEnd.material->expressionRegisters[specularStage->colorStage.registers[1]] * 2.0f;
		specularColor[2] *= backEnd.material->expressionRegisters[specularStage->colorStage.registers[2]] * 2.0f;

		specularPower = specularStage->specularPower;
		specularScale = specularStage->specularScale;
	}
	else
	{
		specularTexture = rg.blackTexture;

		Matrix4_Identity(specularMatrix);

		specularColor[0] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[0]] * lightScale;
		specularColor[1] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[1]] * lightScale;
		specularColor[2] = backEnd.lightMaterial->expressionRegisters[backEnd.lightStage->colorStage.registers[2]] * lightScale;

		specularPower = 16.0f;
		specularScale = 2.0f;
	}

	// Set up the arrays
	qglEnableClientState(GL_NORMAL_ARRAY);
	qglNormalPointer(GL_FLOAT, sizeof(vertexArray_t), backEnd.vertexBuffer->pointer + BUFFER_OFFSET_NORMAL);

	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
	qglTexCoordPointer(2, GL_FLOAT, sizeof(vertexArray_t), backEnd.vertexBuffer->pointer + BUFFER_OFFSET_ST);

	qglEnableClientState(GL_COLOR_ARRAY);
	qglColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertexArray_t), backEnd.vertexBuffer->pointer + BUFFER_OFFSET_COLOR);

	qglEnableVertexAttribArrayARB(10);
	qglVertexAttribPointerARB(10, 3, GL_FLOAT, false, sizeof(vertexArray_t), backEnd.vertexBuffer->pointer + BUFFER_OFFSET_TANGENT0);

	qglEnableVertexAttribArrayARB(11);
	qglVertexAttribPointerARB(11, 3, GL_FLOAT, false, sizeof(vertexArray_t), backEnd.vertexBuffer->pointer + BUFFER_OFFSET_TANGENT1);

	if (backEnd.lightMaterial->ambientLight)
	{
		GL_BindProgram(rg.ambientProgram);

		program = rg.ambientProgram;

		// Vertex shader
		qglUniform1fARB(qglGetUniformLocationARB(program->programId, "u_ColorModulate"), colorModulate);
		qglUniform1fARB(qglGetUniformLocationARB(program->programId, "u_ColorAdd"), colorAdd);

		qglUniformMatrix4fvARB(qglGetUniformLocationARB(program->programId, "u_BumpMatrix"), 1, FALSE, bumpMatrix);
		qglUniformMatrix4fvARB(qglGetUniformLocationARB(program->programId, "u_DiffuseMatrix"), 1, FALSE, diffuseMatrix);
		qglUniformMatrix4fvARB(qglGetUniformLocationARB(program->programId, "u_LocalLightMatrix"), 1, FALSE, backEnd.localParms.lightMatrix);

		// Fragment shader
		qglUniform3fvARB(qglGetUniformLocationARB(program->programId, "u_DiffuseColor"), 1, diffuseColor);

		GL_BindProgramTexture(0, bumpTexture);
		GL_BindProgramTexture(1, diffuseTexture);
		GL_BindProgramTexture(2, backEnd.lightStage->textureStage.texture);
		GL_BindProgramTexture(3, backEnd.lightMaterial->lightFalloffImage);

		qglUniform1iARB(qglGetUniformLocationARB(program->programId, "u_BumpMap"), 0);
		qglUniform1iARB(qglGetUniformLocationARB(program->programId, "u_DiffuseMap"), 1);
		qglUniform1iARB(qglGetUniformLocationARB(program->programId, "u_LightProjectionMap"), 2);
		qglUniform1iARB(qglGetUniformLocationARB(program->programId, "u_LightFalloffMap"), 3);
	}
	else
	{
		GL_BindProgram(rg.interactionProgram);

		program = rg.interactionProgram;

		// Vertex shader
		qglUniform3fvARB(qglGetUniformLocationARB(program->programId, "u_LocalViewOrigin"), 1, backEnd.localParms.viewOrigin);
		qglUniform3fvARB(qglGetUniformLocationARB(program->programId, "u_LocalLightOrigin"), 1, backEnd.localParms.lightOrigin);

		qglUniform1fARB(qglGetUniformLocationARB(program->programId, "u_ColorModulate"), colorModulate);
		qglUniform1fARB(qglGetUniformLocationARB(program->programId, "u_ColorAdd"), colorAdd);

		qglUniformMatrix4fvARB(qglGetUniformLocationARB(program->programId, "u_BumpMatrix"), 1, FALSE, bumpMatrix);
		qglUniformMatrix4fvARB(qglGetUniformLocationARB(program->programId, "u_DiffuseMatrix"), 1, FALSE, diffuseMatrix);
		qglUniformMatrix4fvARB(qglGetUniformLocationARB(program->programId, "u_SpecularMatrix"), 1, FALSE, specularMatrix);
		qglUniformMatrix4fvARB(qglGetUniformLocationARB(program->programId, "u_LocalLightMatrix"), 1, FALSE, backEnd.localParms.lightMatrix);

		// Fragment shader
		qglUniform3fvARB(qglGetUniformLocationARB(program->programId, "u_DiffuseColor"), 1, diffuseColor);
		qglUniform3fvARB(qglGetUniformLocationARB(program->programId, "u_SpecularColor"), 1, specularColor);

		qglUniform2fARB(qglGetUniformLocationARB(program->programId, "u_SpecularParms"), specularPower, specularScale);

		GL_BindProgramTexture(0, bumpTexture);
		GL_BindProgramTexture(1, diffuseTexture);
		GL_BindProgramTexture(2, specularTexture);
		GL_BindProgramTexture(3, backEnd.lightStage->textureStage.texture);
		GL_BindProgramTexture(4, backEnd.lightMaterial->lightFalloffImage);

		qglUniform1iARB(qglGetUniformLocationARB(program->programId, "u_BumpMap"), 0);
		qglUniform1iARB(qglGetUniformLocationARB(program->programId, "u_DiffuseMap"), 1);
		qglUniform1iARB(qglGetUniformLocationARB(program->programId, "u_SpecularMap"), 2);
		qglUniform1iARB(qglGetUniformLocationARB(program->programId, "u_LightProjectionMap"), 3);
		qglUniform1iARB(qglGetUniformLocationARB(program->programId, "u_LightFalloffMap"), 4);
	}

	// Draw it
	RB_DrawElements();

	GL_SelectTexture(0);

	qglDisableVertexAttribArrayARB(11);
	qglDisableVertexAttribArrayARB(10);

	qglDisableClientState(GL_COLOR_ARRAY);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglDisableClientState(GL_NORMAL_ARRAY);
}